#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
监控规则引擎

定义和管理舆论监控的各种规则。
"""

import json
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any, Callable
from dataclasses import dataclass, asdict
from enum import Enum

class RuleType(Enum):
    """规则类型"""
    SENTIMENT_THRESHOLD = "sentiment_threshold"  # 情感阈值规则
    COMMENT_COUNT = "comment_count"             # 评论数量规则
    KEYWORD_DETECTION = "keyword_detection"     # 关键词检测规则
    USER_INFLUENCE = "user_influence"           # 用户影响力规则
    TIME_PATTERN = "time_pattern"               # 时间模式规则
    TREND_ANALYSIS = "trend_analysis"           # 趋势分析规则

class Severity(Enum):
    """告警严重程度"""
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"
    INFO = "info"

@dataclass
class MonitoringRule:
    """监控规则"""
    id: str
    name: str
    rule_type: RuleType
    conditions: Dict[str, Any]  # 规则条件
    severity: Severity
    enabled: bool = True
    description: str = ""
    created_time: str = ""
    last_triggered: str = ""
    trigger_count: int = 0
    cooldown_minutes: int = 30  # 冷却时间（分钟）
    
    def __post_init__(self):
        if not self.created_time:
            self.created_time = datetime.now().isoformat()

class MonitoringRules:
    """监控规则管理器"""
    
    def __init__(self):
        self.logger = logging.getLogger(__name__)
        self.rules: Dict[str, MonitoringRule] = {}
        self.rule_evaluators: Dict[RuleType, Callable] = {}
        
        # 注册规则评估器
        self._register_evaluators()
        
        # 加载默认规则
        self._load_default_rules()
        
        self.logger.info("监控规则引擎初始化完成")
    
    def _register_evaluators(self):
        """注册规则评估器"""
        self.rule_evaluators = {
            RuleType.SENTIMENT_THRESHOLD: self._evaluate_sentiment_threshold,
            RuleType.COMMENT_COUNT: self._evaluate_comment_count,
            RuleType.KEYWORD_DETECTION: self._evaluate_keyword_detection,
            RuleType.USER_INFLUENCE: self._evaluate_user_influence,
            RuleType.TIME_PATTERN: self._evaluate_time_pattern,
            RuleType.TREND_ANALYSIS: self._evaluate_trend_analysis
        }
    
    def _load_default_rules(self):
        """加载默认监控规则"""
        default_rules = [
            # 情感阈值规则
            MonitoringRule(
                id="sentiment_critical",
                name="严重负面情感告警",
                rule_type=RuleType.SENTIMENT_THRESHOLD,
                conditions={
                    "negative_rate": 0.6,  # 负面评论占比
                    "min_comments": 10,    # 最小评论数
                    "threat_level": 4      # 威胁等级
                },
                severity=Severity.CRITICAL,
                description="当负面评论占比超过60%且威胁等级≥4时触发",
                cooldown_minutes=60
            ),
            
            MonitoringRule(
                id="sentiment_high",
                name="高负面情感告警",
                rule_type=RuleType.SENTIMENT_THRESHOLD,
                conditions={
                    "negative_rate": 0.4,
                    "min_comments": 5,
                    "threat_level": 3
                },
                severity=Severity.HIGH,
                description="当负面评论占比超过40%且威胁等级≥3时触发",
                cooldown_minutes=30
            ),
            
            # 评论数量规则
            MonitoringRule(
                id="comment_surge_critical",
                name="评论数量激增告警",
                rule_type=RuleType.COMMENT_COUNT,
                conditions={
                    "total_comments": 100,      # 总评论数
                    "negative_comments": 50,    # 负面评论数
                    "time_window": 60          # 时间窗口（分钟）
                },
                severity=Severity.CRITICAL,
                description="短时间内出现大量负面评论时触发"
            ),
            
            # 关键词检测规则
            MonitoringRule(
                id="serious_keywords",
                name="严重负面关键词检测",
                rule_type=RuleType.KEYWORD_DETECTION,
                conditions={
                    "keywords": [
                        "食物中毒", "拉肚子", "有虫子", "发霉", "变质",
                        "黑店", "坑人", "宰客", "恶心", "垃圾店"
                    ],
                    "min_matches": 3,  # 最少匹配次数
                    "user_influence_threshold": 5  # 用户影响力阈值
                },
                severity=Severity.CRITICAL,
                description="检测到严重负面关键词时触发",
                cooldown_minutes=15
            ),
            
            # 用户影响力规则
            MonitoringRule(
                id="influential_negative",
                name="高影响力用户负面评论",
                rule_type=RuleType.USER_INFLUENCE,
                conditions={
                    "min_influence_score": 7,  # 最小影响力分数
                    "sentiment": "negative",   # 情感类别
                    "threat_level": 2         # 威胁等级
                },
                severity=Severity.HIGH,
                description="高影响力用户发表负面评论时触发",
                cooldown_minutes=20
            ),
            
            # 时间模式规则
            MonitoringRule(
                id="night_negative_surge",
                name="夜间负面评论激增",
                rule_type=RuleType.TIME_PATTERN,
                conditions={
                    "time_range": [22, 6],     # 时间范围（22点-6点）
                    "negative_rate": 0.5,      # 负面评论占比
                    "min_comments": 10         # 最小评论数
                },
                severity=Severity.MEDIUM,
                description="夜间时段负面评论异常增加时触发"
            )
        ]
        
        for rule in default_rules:
            self.rules[rule.id] = rule
        
        self.logger.info(f"已加载 {len(default_rules)} 条默认规则")
    
    async def evaluate_rules(self, data: Dict, target_info: Dict = None) -> List[Dict]:
        """评估所有规则"""
        triggered_alerts = []
        
        for rule in self.rules.values():
            if not rule.enabled:
                continue
            
            # 检查冷却时间
            if self._is_in_cooldown(rule):
                continue
            
            try:
                # 获取规则评估器
                evaluator = self.rule_evaluators.get(rule.rule_type)
                if not evaluator:
                    self.logger.warning(f"未找到规则评估器: {rule.rule_type}")
                    continue
                
                # 评估规则
                is_triggered = await evaluator(rule, data, target_info)
                
                if is_triggered:
                    alert = self._create_alert(rule, data, target_info)
                    triggered_alerts.append(alert)
                    
                    # 更新规则状态
                    rule.last_triggered = datetime.now().isoformat()
                    rule.trigger_count += 1
                    
                    self.logger.info(f"规则触发: {rule.name} (ID: {rule.id})")
                
            except Exception as e:
                self.logger.error(f"评估规则失败 {rule.id}: {e}")
        
        return triggered_alerts
    
    def _is_in_cooldown(self, rule: MonitoringRule) -> bool:
        """检查规则是否在冷却期"""
        if not rule.last_triggered:
            return False
        
        last_triggered = datetime.fromisoformat(rule.last_triggered)
        cooldown_end = last_triggered + timedelta(minutes=rule.cooldown_minutes)
        
        return datetime.now() < cooldown_end
    
    def _create_alert(self, rule: MonitoringRule, data: Dict, target_info: Dict) -> Dict:
        """创建告警"""
        target_name = target_info.get('name', '未知目标') if target_info else '未知目标'
        
        return {
            "rule_id": rule.id,
            "rule_name": rule.name,
            "type": rule.rule_type.value,
            "severity": rule.severity.value,
            "title": f"{rule.name}: {target_name}",
            "content": self._generate_alert_content(rule, data),
            "target_id": target_info.get('id', '') if target_info else '',
            "timestamp": datetime.now().isoformat(),
            "data": data
        }
    
    def _generate_alert_content(self, rule: MonitoringRule, data: Dict) -> str:
        """生成告警内容"""
        content_parts = [rule.description]
        
        # 添加数据详情
        if rule.rule_type == RuleType.SENTIMENT_THRESHOLD:
            negative_rate = data.get('negative_comments', 0) / max(data.get('comments_analyzed', 1), 1)
            content_parts.append(f"负面评论占比: {negative_rate:.1%}")
            content_parts.append(f"总评论数: {data.get('comments_analyzed', 0)}")
            content_parts.append(f"负面评论数: {data.get('negative_comments', 0)}")
            content_parts.append(f"高风险评论数: {data.get('high_risk_comments', 0)}")
        
        elif rule.rule_type == RuleType.COMMENT_COUNT:
            content_parts.append(f"总评论数: {data.get('comments_analyzed', 0)}")
            content_parts.append(f"负面评论数: {data.get('negative_comments', 0)}")
        
        elif rule.rule_type == RuleType.KEYWORD_DETECTION:
            matched_keywords = data.get('matched_keywords', [])
            if matched_keywords:
                content_parts.append(f"匹配关键词: {', '.join(matched_keywords[:5])}")
        
        return "\\n".join(content_parts)
    
    async def _evaluate_sentiment_threshold(self, rule: MonitoringRule, 
                                          data: Dict, target_info: Dict) -> bool:
        """评估情感阈值规则"""
        conditions = rule.conditions
        
        # 检查最小评论数
        comments_analyzed = data.get('comments_analyzed', 0)
        if comments_analyzed < conditions.get('min_comments', 0):
            return False
        
        # 检查负面评论占比
        negative_comments = data.get('negative_comments', 0)
        negative_rate = negative_comments / max(comments_analyzed, 1)
        
        if negative_rate < conditions.get('negative_rate', 0):
            return False
        
        # 检查威胁等级
        risk_level = data.get('risk_level', 'low')
        threat_level_map = {'low': 1, 'medium': 2, 'high': 3, 'critical': 4}
        current_threat = threat_level_map.get(risk_level, 1)
        
        return current_threat >= conditions.get('threat_level', 1)
    
    async def _evaluate_comment_count(self, rule: MonitoringRule,
                                    data: Dict, target_info: Dict) -> bool:
        """评估评论数量规则"""
        conditions = rule.conditions
        
        total_comments = data.get('comments_analyzed', 0)
        negative_comments = data.get('negative_comments', 0)
        
        return (total_comments >= conditions.get('total_comments', 0) and
                negative_comments >= conditions.get('negative_comments', 0))
    
    async def _evaluate_keyword_detection(self, rule: MonitoringRule,
                                        data: Dict, target_info: Dict) -> bool:
        """评估关键词检测规则"""
        conditions = rule.conditions
        keywords = conditions.get('keywords', [])
        min_matches = conditions.get('min_matches', 1)
        
        # 模拟关键词匹配（实际应该从评论文本中检测）
        # 这里简化处理，基于负面评论数量来模拟
        negative_comments = data.get('negative_comments', 0)
        matched_count = min(negative_comments // 2, len(keywords))  # 简化计算
        
        return matched_count >= min_matches
    
    async def _evaluate_user_influence(self, rule: MonitoringRule,
                                     data: Dict, target_info: Dict) -> bool:
        """评估用户影响力规则"""
        conditions = rule.conditions
        
        # 模拟高影响力用户检测
        # 实际应该检查评论中是否有高影响力用户发表负面评论
        high_risk_comments = data.get('high_risk_comments', 0)
        risk_level = data.get('risk_level', 'low')
        
        return (high_risk_comments >= 2 and 
                risk_level in ['high', 'critical'])
    
    async def _evaluate_time_pattern(self, rule: MonitoringRule,
                                   data: Dict, target_info: Dict) -> bool:
        """评估时间模式规则"""
        conditions = rule.conditions
        time_range = conditions.get('time_range', [])
        
        if not time_range or len(time_range) != 2:
            return False
        
        current_hour = datetime.now().hour
        start_hour, end_hour = time_range
        
        # 检查是否在指定时间范围内
        if start_hour <= end_hour:
            in_time_range = start_hour <= current_hour <= end_hour
        else:  # 跨天的时间范围
            in_time_range = current_hour >= start_hour or current_hour <= end_hour
        
        if not in_time_range:
            return False
        
        # 检查其他条件
        comments_analyzed = data.get('comments_analyzed', 0)
        negative_comments = data.get('negative_comments', 0)
        
        if comments_analyzed < conditions.get('min_comments', 0):
            return False
        
        negative_rate = negative_comments / max(comments_analyzed, 1)
        return negative_rate >= conditions.get('negative_rate', 0)
    
    async def _evaluate_trend_analysis(self, rule: MonitoringRule,
                                     data: Dict, target_info: Dict) -> bool:
        """评估趋势分析规则"""
        # 趋势分析需要历史数据，这里简化处理
        return False
    
    def add_rule(self, rule: MonitoringRule) -> bool:
        """添加监控规则"""
        try:
            self.rules[rule.id] = rule
            self.logger.info(f"已添加监控规则: {rule.name} (ID: {rule.id})")
            return True
        except Exception as e:
            self.logger.error(f"添加监控规则失败: {e}")
            return False
    
    def remove_rule(self, rule_id: str) -> bool:
        """删除监控规则"""
        if rule_id in self.rules:
            rule_name = self.rules[rule_id].name
            del self.rules[rule_id]
            self.logger.info(f"已删除监控规则: {rule_name} (ID: {rule_id})")
            return True
        return False
    
    def update_rule(self, rule_id: str, updates: Dict) -> bool:
        """更新监控规则"""
        if rule_id not in self.rules:
            return False
        
        try:
            rule = self.rules[rule_id]
            for key, value in updates.items():
                if hasattr(rule, key):
                    setattr(rule, key, value)
            
            self.logger.info(f"已更新监控规则: {rule.name} (ID: {rule_id})")
            return True
        except Exception as e:
            self.logger.error(f"更新监控规则失败: {e}")
            return False
    
    def get_rule(self, rule_id: str) -> Optional[Dict]:
        """获取指定规则"""
        if rule_id in self.rules:
            return asdict(self.rules[rule_id])
        return None
    
    def get_all_rules(self) -> List[Dict]:
        """获取所有规则"""
        return [asdict(rule) for rule in self.rules.values()]
    
    def get_enabled_rules(self) -> List[Dict]:
        """获取启用的规则"""
        return [asdict(rule) for rule in self.rules.values() if rule.enabled]
    
    def enable_rule(self, rule_id: str) -> bool:
        """启用规则"""
        if rule_id in self.rules:
            self.rules[rule_id].enabled = True
            return True
        return False
    
    def disable_rule(self, rule_id: str) -> bool:
        """禁用规则"""
        if rule_id in self.rules:
            self.rules[rule_id].enabled = False
            return True
        return False
    
    def get_rule_statistics(self) -> Dict:
        """获取规则统计信息"""
        total_rules = len(self.rules)
        enabled_rules = sum(1 for rule in self.rules.values() if rule.enabled)
        
        # 按类型统计
        type_counts = {}
        for rule in self.rules.values():
            rule_type = rule.rule_type.value
            type_counts[rule_type] = type_counts.get(rule_type, 0) + 1
        
        # 按严重程度统计
        severity_counts = {}
        for rule in self.rules.values():
            severity = rule.severity.value
            severity_counts[severity] = severity_counts.get(severity, 0) + 1
        
        # 触发统计
        total_triggers = sum(rule.trigger_count for rule in self.rules.values())
        most_triggered = max(self.rules.values(), key=lambda r: r.trigger_count, default=None)
        
        return {
            "total_rules": total_rules,
            "enabled_rules": enabled_rules,
            "disabled_rules": total_rules - enabled_rules,
            "type_distribution": type_counts,
            "severity_distribution": severity_counts,
            "total_triggers": total_triggers,
            "most_triggered_rule": {
                "id": most_triggered.id,
                "name": most_triggered.name,
                "trigger_count": most_triggered.trigger_count
            } if most_triggered else None
        }
    
    def export_rules(self, filepath: str) -> bool:
        """导出规则配置"""
        try:
            rules_data = [asdict(rule) for rule in self.rules.values()]
            with open(filepath, 'w', encoding='utf-8') as f:
                json.dump(rules_data, f, ensure_ascii=False, indent=2)
            
            self.logger.info(f"规则配置已导出到: {filepath}")
            return True
        except Exception as e:
            self.logger.error(f"导出规则配置失败: {e}")
            return False
    
    def import_rules(self, filepath: str) -> bool:
        """导入规则配置"""
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                rules_data = json.load(f)
            
            imported_count = 0
            for rule_dict in rules_data:
                # 转换枚举值
                rule_dict['rule_type'] = RuleType(rule_dict['rule_type'])
                rule_dict['severity'] = Severity(rule_dict['severity'])
                
                rule = MonitoringRule(**rule_dict)
                self.rules[rule.id] = rule
                imported_count += 1
            
            self.logger.info(f"已导入 {imported_count} 条规则")
            return True
        except Exception as e:
            self.logger.error(f"导入规则配置失败: {e}")
            return False